/*------------------------------------------------------------------------------*\
**   Copyright 2008 Jonhnny Weslley                                             **
**                                                                              **
**   Licensed under the Apache License, Version 2.0 (the "License");            **
**   you may not use this file except in compliance with the License.           **
**   You may obtain a copy of the License at                                    **
**                                                                              **
**       http://www.apache.org/licenses/LICENSE-2.0                             **
**                                                                              **
**   Unless required by applicable law or agreed to in writing, software        **
**   distributed under the License is distributed on an "AS IS" BASIS,          **
**   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   **
**   See the License for the specific language governing permissions and        **
**   limitations under the License.                                             **
\*------------------------------------------------------------------------------*/

package shibernate

import org.hibernate.Criteria
import org.hibernate.criterion.{Criterion, Order, Expression, Restrictions}

abstract class Parameter {

  private val PROPERTY_SEPARATOR = '.'
  private type CriteriaTable = scala.collection.mutable.Map[String, Criteria]

  val name: String
  val value: Any
  val isLogic = false
  var isComponent: Boolean = _
  private val ps: ParameterSpliter = new ParameterSpliter

  def prefix = {
    val n = name
    val i = n.indexOf(PROPERTY_SEPARATOR)
    val index = if (i > 0) i else n.length
    n.substring(0, index)
  }

  def suffix =
    if (isComponent) name
    else {
      val n = name
      val i = n.lastIndexOf(PROPERTY_SEPARATOR)
      n.substring(i + 1)
    }

  def complete(c: Criteria)

  def process(c: Criteria, table: CriteriaTable) {
    ps(c) = table
  }

  private class ParameterSpliter {

    var token: String = _
    var criteria: Criteria = _
    var table: CriteriaTable = _

    def update(c: Criteria, ct: CriteriaTable) {
      token = name
      criteria = c
      table = ct
      execute()
    }

    def execute() {
      if (!isComponent && token.indexOf(PROPERTY_SEPARATOR) > 0) {
        splitParameter()
      } else {
        complete(criteria)
      }
    }

    def splitParameter() {
      val offSet = token.indexOf(PROPERTY_SEPARATOR)
      val alias = token.substring(0, offSet)
      token = token.substring(offSet + 1, token.length)
      criteria = createCriteria(alias)
      execute()
    }

    def createCriteria(alias: String): Criteria = {
      if (isComponent) {
        return criteria
      }
      var subCriteria = table(alias)
      if (subCriteria eq null) {
        subCriteria = criteria.createCriteria(alias)
      }
      table(alias) = subCriteria
      subCriteria
    }

  }

}

abstract class CriterionParameter extends Parameter {
  def criterion: Criterion
  def complete(c: Criteria) = c add criterion
}

case class Equals(name: String, value: Any) extends CriterionParameter {
  def criterion = Restrictions.eq(suffix, value)
}

case class NotEquals(name: String, value: Any) extends CriterionParameter {
  def criterion = Restrictions.ne(suffix, value)
}

case class LowerEquals(name: String, value: Any) extends CriterionParameter {
  def criterion = Restrictions.le(suffix, value)
}

case class LowerThan(name: String, value: Any) extends CriterionParameter {
  def criterion = Restrictions.lt(suffix, value)
}

case class GreaterEquals(name: String, value: Any) extends CriterionParameter {
  def criterion = Restrictions.ge(suffix, value)
}

case class GreaterThan(name: String, value: Any) extends CriterionParameter {
  def criterion = Restrictions.gt(suffix, value)
}

case class IsNull(name: String) extends CriterionParameter {
  val value = null
  def criterion = Restrictions.isNull(suffix)
}

case class IsNotNull(name: String) extends CriterionParameter {
  val value = null
  def criterion = Restrictions.isNotNull(suffix)
}

case class Between(name: String, value: Any, value2: Any) extends CriterionParameter {
  def criterion = Restrictions.between(suffix, value, value2)
}

case class Like(name: String, value: Any) extends CriterionParameter {
  def criterion = Restrictions.ilike(suffix, value)
}

case class OrderBy(name: String, asc: Boolean) extends Parameter {
  val value = null
  def complete(c: Criteria) = {
    val ord = if (asc) Order asc suffix else Order desc suffix
    c addOrder ord
  }
}

case class And(value1: CriterionParameter, value2: CriterionParameter) extends CriterionParameter {

  require(value1 ne null)
  require(value2 ne null)

  val name = "<AND>"
  val value = value1
  override val isLogic = true

  def criterion = {
    val c1 = value1.criterion
    val c2 = value2.criterion
    Restrictions.and(c1, c2)
  }
}

case class Or(value1: CriterionParameter, value2: CriterionParameter) extends CriterionParameter {

  require(value1 ne null)
  require(value2 ne null)

  val name = "<OR>"
  val value = value1
  override val isLogic = true

  def criterion = {
    val c1 = value1.criterion
    val c2 = value2.criterion
    Restrictions.or(c1, c2)
  }
}

case class Not(value: CriterionParameter) extends CriterionParameter {

  require(value ne null)

  val name = "<NOT>"
  override val isLogic = true

  def criterion = {
    val c = value.criterion
    Restrictions.not(c)
  }
}
